<template>
  <div
    class="searcher"
    @mouseenter="mouseenter"
    @mouseleave="isMouseIn = false"
  >
    <div class="u-input-wrapper">
      <input
        class="u-input"
        :class="{ 'u-searching': isShowOptions }"
        type="text"
        :placeholder="placeholder"
        @focus="focusInput"
        @blur="blurInput"
        @input="inputing"
        v-model="inputText"
      />
      <div class="u-result" :style="{ maxHeight }" v-show="isShowOptions">
        <!-- slot error -->
        <slot name="error" v-if="isShowError">
          <div class="u-error">
            <el-icon class="u-icon"><WarningFilled /></el-icon>
            <span>{{ errorText }}</span>
          </div>
        </slot>
        <!-- slot loading -->
        <slot name="loading" v-else-if="loading">
          <div class="u-loading">
            <el-icon class="u-icon"><Loading /></el-icon>
            <span>{{ loadingText }}</span>
          </div>
        </slot>
        <!-- slot empty -->
        <slot name="empty" v-else-if="options.length <= 0">
          <div class="u-empty">
            {{ emptyText }}
          </div>
        </slot>

        <!-- slot default -->
        <slot :options="options" v-else>
          <div class="u-options">
            <div
              class="u-option"
              v-for="option in options"
              @click="() => clickOption(option)"
            >
              {{ getLabel(option) }}
            </div>
          </div>
        </slot>
      </div>
      <div class="u-clear" v-show="inputText" @click="clickClear">
        <el-icon :size="20" color="#909399"><CircleClose /></el-icon>
      </div>
    </div>
    <button class="u-btn" @click="clickSubmit">
      <!-- slot submit -->
      <slot name="submit">{{ submitText }}</slot>
    </button>
  </div>
</template>

<script setup name="Searcher">
/**
 * 本组件提供5种插槽，一般不需要用到：
     - error 错误提示
     - empty 搜索无结果
     - loading 加载中
     - submit 提交按钮
     - default 下拉选项，scope中有options属性
 
 * 如要修改组件样式，可通过 :deep() 进行修改
 
 * 案例代码：
    <Searcher
      :getLabel="(item) => item.companyName"
      :getOptions="getOptions"
      @submit="(item) => submit(item)"
    >
      <template #empty>无数据</template>
      <template #loading>加载中</template>
      <template #error>有错误</template>
      <template #submit>搜索</template>
      <template #default="{ options }">
        <div v-for="item in options">{{ item.companyName }}</div>
      </template>
    </Searcher>
 */
import { computed, ref } from 'vue'
import { watchDebounced } from '@vueuse/core'
import { ElMessage } from 'element-plus'

const props = defineProps({
  getLabel: Function, // 必须，从数据对象中获取展现的文字
  getOptions: Function, // 必须，获取下拉数据；接收输入的文字，返回数组；可以是异步函数
  minWordLen: {
    type: Number, // 触发关键词联想的最小关键词长度
    default: 1,
  },
  maxHeight: {
    type: String,
    default: '300px',
  },
  placeholder: {
    type: String,
    default: '请输入关键字',
  },
  loadingText: {
    type: String,
    default: '搜索中',
  },
  emptyText: {
    type: String,
    default: '暂无数据，请更换关键词',
  },
  errorText: {
    type: String,
    default: '请选择选项中的企业',
  },
  submitText: {
    type: String,
    default: '查询企业',
  },
})

const emit = defineEmits(['submit', 'change'])

// 输入的关键字
const inputText = ref('')
// 下拉列表
const options = ref([])
// 获取下拉项的加载状态
const loading = ref(false)
// 当前选中的下拉项
const selectedOption = ref(null)

// 文本框是否处于聚焦状态
const isFocus = ref(false)
const isMouseIn = ref(false)
// 是否显示错误文案
const isShowError = ref(false)
// 是否显示下拉框
const isShowOptions = computed(() => {
  return (
    isShowError.value ||
    (inputText.value &&
      inputText.value.length >= props.minWordLen &&
      isFocus.value)
  )
})

// 文本框聚焦
function focusInput() {
  isMouseIn.value = true
  isFocus.value = true
}

// 文本框失焦
function blurInput() {
  if (!isMouseIn.value) {
    isFocus.value = false
  }
}

function mouseenter() {
  if (isFocus.value) {
    isMouseIn.value = true
  }
}

// 文字输入
function inputing() {
  selectedOption.value = null
  isShowError.value = false
  emit('change', selectedOption.value)
}

// 点击下拉项
function clickOption(option) {
  selectedOption.value = option
  inputText.value = props.getLabel(option)
  isShowError.value = false
  isFocus.value = false
  emit('change', selectedOption.value)
}

// 点击清空
function clickClear() {
  selectedOption.value = null
  inputText.value = ''
  isShowError.value = false
  emit('change', selectedOption.value)
}

// 点击提交
function clickSubmit() {
  if (inputText.value) {
    if (selectedOption.value) {
      emit('submit', selectedOption.value)
    } else {
      isShowError.value = true
    }
  } else {
    ElMessage.warning('请先输入关键字')
  }
}

// 文字变更，则以防抖方式查询数据
watchDebounced(
  inputText,
  async (v) => {
    if (v && v.length >= props.minWordLen) {
      loading.value = true
      options.value = await props.getOptions(v)
      loading.value = false
    } else {
      loading.value = false
      options.value = []
    }
  },
  { debounce: 500 }
)
</script>

<style lang="scss" scoped>
$inputHeight: 56px;
$borderRadius: 8px;

.searcher {
  width: 900px;
  display: flex;
  .u-input-wrapper {
    position: relative;
    flex: 1;
    .u-input {
      position: relative;
      padding-left: 30px;
      width: 100%;
      height: $inputHeight;
      font-size: 18px;
      color: #333;
      border: none;
      border-radius: $borderRadius 0 0 $borderRadius;
      background-color: #fff;
      outline: none;
      z-index: 2;
      &.u-searching {
        border-radius: $borderRadius 0 0 0;
      }
    }
    .u-result {
      position: absolute;
      top: $inputHeight;
      padding: 0 0 15px;
      width: 100%;
      font-size: 16px;
      background-color: #fff;
      border-radius: 0 0 $borderRadius $borderRadius;
      box-shadow: 0px 8px 30px -6px rgba(121, 133, 150, 0.6);
      z-index: 1;
      overflow: auto;
      .u-loading,
      .u-error,
      .u-empty {
        display: flex;
        align-items: center;
        margin-top: 7px;
        padding: 9px 30px;
        color: #999;
        .u-icon {
          margin-right: 6px;
        }
      }
      .u-error {
        background-color: rgba(240, 102, 127, 0.1);
        color: #f0667f;
      }
      .u-option {
        padding: 7px 30px;
        cursor: pointer;
        &:hover {
          color: var(--el-color-primary);
          background-color: #f4f5f8;
        }
      }
    }
    .u-clear {
      position: absolute;
      right: 10px;
      top: 20px;
      z-index: 2;
      cursor: pointer;
      &:hover {
        opacity: 0.8;
      }
    }
  }
  .u-btn {
    width: 150px;
    height: $inputHeight;
    font-size: 18px;
    color: #fff;
    letter-spacing: 1px;
    background-color: #ff8164;
    border: none;
    outline: none;
    border-radius: 0 $borderRadius $borderRadius 0;
    cursor: pointer;
    &:hover {
      background-color: #faa491;
    }
  }
}
</style>
